-
Notifications
You must be signed in to change notification settings - Fork 1
add product tests #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds a comprehensive tests/test_product.py suite covering Product API CRUD operations, pagination, and token-related error handling. Tests include creation, duplicate handling, retrieval (single and list), update, delete, and parameterized auth error scenarios. Fixtures and helpers manage DB setup, product creation, and validations. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (8)
tests/test_product.py (8)
32-45: Helper assertions are clear; consider optional description check.
If you want slightly stronger guarantees, allow an optionalexpected_descriptionand assert it when provided. Keeps current call sites unchanged.- def _verify_product_in_db(self, name, should_exist=True): + def _verify_product_in_db(self, name, should_exist=True, expected_description=None): with self.client.application.app_context(): product = Product.query.filter_by(name=name).first() if should_exist: assert product is not None assert product.name == name + if expected_description is not None: + assert product.description == expected_description return product else: assert product is None
64-74: Get-by-id happy-path is correct; consider adding a 404 test for a non-existent product.
Adding a negative test improves coverage of error paths and keeps contract stable.I can draft a
test_get_product_not_foundthat asserts 404 for/product/9999.
105-116: Delete flow is correct; consider asserting 204 No Content if you adopt that semantic.
Current 200/then-404-on-read is fine; 204 is another common choice for deletions without body.- assert delete_resp.status_code == 200 + assert delete_resp.status_code in (200, 204)
133-157: Token error coverage for update is solid; minor robustness tweak optional.
Some JWT setups return 422 for malformed tokens. If that applies in your stack, passstatus_code=422for theinvalid_tokencase.- (lambda self: utils.get_invalid_token_headers(), "invalid_token"), + (lambda self: utils.get_invalid_token_headers(), "invalid_token"), # add status_code=422 if your JWT lib uses 422
88-104: Optional: align update endpoint with REST conventions
Theupdate_productroute and its test currently return and expect HTTP 201. While this matches the current implementation, the conventional status for an update is 200 (with a response body) or 204 (no content). If you’d like to follow REST best practices, consider updating both the route and the test:• In app/routes.py, change the return status code
- return jsonify(product.to_json()), 201 + return jsonify(product.to_json()), 200• In tests/test_product.py (around line 93), adjust the assertion
- assert update_resp.status_code == 201 + assert update_resp.status_code == 200
56-63: Enhance duplicate‐name handling increate_product(optional)Currently, submitting a product with an existing name triggers the generic
except:block inapp/routes.py, yielding a 500 response. While your test correctly asserts this behavior, it’s better API design to treat duplicates as client errors.• In app/routes.py at
create_product(around line 712):
- Replace the blanket
except:with a targetedexcept IntegrityError:
– Roll back the session
– Return a JSON error and 409 Conflict (or 400 Bad Request)- Keep a final catch-all for unexpected errors, still returning 500
Example adjustment:
try: db.session.add(product) - db.session.commit() - return jsonify(product.to_json()), 201 - except: - return "Error occured", 500 + db.session.commit() + return jsonify(product.to_json()), 201 + except IntegrityError as e: + db.session.rollback() + return jsonify({"error": "Product name already exists"}), 409 + except Exception: + db.session.rollback() + return jsonify({"error": "Internal server error"}), 500• Once the endpoint returns 409 for duplicates, update your test:
- assert response.status_code == 500 + assert response.status_code == 409 + payload = response.get_json() + assert payload and "error" in payloadFeel free to leave the current 500 assertion in place until the API change is merged. Let me know if you’d like help opening an issue to standardize duplicate‐name error handling across all create/update routes.
178-195: Decouple pagination in tests from hardcodedper_page
I verified that the/productsendpoint in app/routes.py (lines 938–942) callsproducts = Product.query.order_by(Product.id.asc()) \ .paginate(page=page, per_page=10, error_out=False) return jsonify({"products": […]}), 200and does not expose any pagination metadata (e.g.
per_page,total) in its JSON response. Relying on the hardcoded page size of 10 makes the test brittle if that value ever changes.To make the test more robust, I recommend the following optional refactor:
• Option A: Assert totals across pages without assuming a specific page size:
# Page 1 resp1 = self.client.get("/products?page=1") assert resp1.status_code == 200 data1 = resp1.get_json() assert "products" in data1 - assert len(data1["products"]) == 10 + page1_count = len(data1["products"]) # Page 2 resp2 = self.client.get("/products?page=2") assert resp2.status_code == 200 data2 = resp2.get_json() assert "products" in data2 - assert len(data2["products"]) == 5 + page2_count = len(data2["products"]) + assert page1_count > 0 + assert page1_count + page2_count == 15• Option B (requires an API change): Modify the endpoint to include pagination metadata in its response, e.g.
{ "products": […], "page": 1, "per_page": 10, "total": 15 }and then assert those fields directly in your tests.
Given the current implementation, Option A is the easiest way to decouple your tests from a fixed
per_page.
17-31: Cache authentication headers increate_productto prevent redundant registration callsThe
create_authenticated_headersfixture always invokesregister_user(which returns HTTP 409 on duplicates) followed bylogin_useron each call. Sinceregister_userisn’t idempotent (seetest_register_duplicate_emailexpecting a 409 on the second call), calling it repeatedly for everycreate_productinvocation incurs unnecessary HTTP overhead and risks flakiness. It’s safe—and more efficient—to callcreate_authenticated_headers()just once per test and reuse the resulting headers.Key locations to update:
- tests/test_product.py (lines 17–31): the
create_productfixture currently callscreate_authenticated_headers()on every_createinvocation whenheadersisNone.- tests/conftest.py (lines 43–49):
create_authenticated_headersunconditionally callsregister_user(email, password).Suggested diff for
create_productfixture intests/test_product.py:@pytest.fixture def create_product(self, create_authenticated_headers): - def _create(name, description=None, subcategories=None, headers=None): - if headers is None: - headers = create_authenticated_headers() + default_headers = None + def _create(name, description=None, subcategories=None, headers=None): + nonlocal default_headers + if headers is None: + if default_headers is None: + default_headers = create_authenticated_headers() + headers = default_headers payload = {"name": name} if description is not None: payload["description"] = description if subcategories is not None: payload["subcategories"] = subcategories return self.client.post( "/product/create", json=payload, headers=headers ) - return _create + return _createThis change ensures:
register_user+login_userare called only once per test (whendefault_headersis first needed).- Subsequent
create_productcalls reuse the sameAuthorizationheader, reducing test runtime and potential conflicts.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
tests/test_product.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tests/test_product.py (4)
app/models.py (2)
Product(95-109)get(30-35)tests/conftest.py (2)
client(15-22)create_authenticated_headers(44-51)tests/utils.py (3)
get_expired_token_headers(17-22)get_invalid_token_headers(25-26)verify_token_error_response(6-10)app/routes.py (1)
update_product(782-845)
🔇 Additional comments (6)
tests/test_product.py (6)
1-5: Imports and module setup look good.
No unnecessary imports and pytest is correctly leveraged.
11-16: Good autouse fixture to enforce a clean DB per test.
Asserting an empty Product table at the start of each test prevents cross-test bleed.
46-55: Create test is solid and aligns with the API shape.
Status code, response fields, and DB verification are appropriate.
75-87: List test is good; order-agnostic membership checks avoid flakiness.
No changes needed. Once header caching is in place, the double-creation overhead will also drop.
117-132: Token error coverage for create is thorough.
Parametrization and post-condition DB checks are on point.
158-177: Token error coverage for delete looks good.
Happy with the invariants being asserted after failure.
Summary by CodeRabbit